package framework.storage;

import framework.config.FileStorageHttpConfig;
import lombok.Getter;
import org.apache.commons.lang3.StringUtils;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URI;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;

/**
 * 使用网络存储的文件存储实现
 */
public class FileStorageHttp implements FileStorage {
    private static final int BUFFER_SIZE = 1 * 1024 * 1024;
    @Getter
    private FileStorageHttpConfig config;

    public FileStorageHttp(FileStorageHttpConfig config) {
        this.config = config;
    }

    protected void setupTimeout(HttpURLConnection connection) {
        if (getConfig().getConnectTimeout() != null) {
            connection.setConnectTimeout(getConfig().getConnectTimeout());
        }
        if (getConfig().getReadTimeout() != null) {
            connection.setReadTimeout(getConfig().getReadTimeout());
        }
    }

    protected void setupAuth(HttpURLConnection connection) {
        if (StringUtils.isNotBlank(getConfig().getAuthorization())) {
            connection.setRequestProperty("Authorization", getConfig().getAuthorization());
        }
    }

    protected void setupConnection(HttpURLConnection connection) {
        // 用户配置用
    }

    protected URI buildUrl(String filePath) {
        return getConfig().uri(filePath);
    }

    @Override
    public long save(InputStream inputStream, String filePath, boolean compression) throws IOException {
        if (compression) {
            filePath += ".gz";
        }
        URI uri = buildUrl(filePath);

        HttpURLConnection connection = (HttpURLConnection) uri.toURL().openConnection();
        connection.setRequestMethod("PUT");
        connection.setDoOutput(true);
        setupTimeout(connection);
        setupAuth(connection);
        setupConnection(connection);

        long len = 0;
        try (OutputStream os = compression
                ? new GZIPOutputStream(connection.getOutputStream())
                : connection.getOutputStream()) {
            byte[] buffer = new byte[BUFFER_SIZE];
            int bytesRead;
            while ((bytesRead = inputStream.read(buffer)) != -1) {
                os.write(buffer, 0, bytesRead);
                len += bytesRead;
            }
        }

        int responseCode = connection.getResponseCode();
        if (responseCode == 200 || responseCode == 201) {
            return len;
        }
        throw new IOException("PUT request failed with response code: " + responseCode);
    }

    @Override
    public InputStream getStream(String filePath, boolean compression) throws IOException {
        if (compression) {
            filePath += ".gz";
        }
        URI uri = buildUrl(filePath);

        HttpURLConnection connection = (HttpURLConnection) uri.toURL().openConnection();
        connection.setRequestMethod("GET");
        setupTimeout(connection);
        setupAuth(connection);
        setupConnection(connection);

        int responseCode = connection.getResponseCode();
        if (responseCode != 200) {
            throw new IOException("GET request failed with response code: " + responseCode);
        }

        InputStream inputStream = connection.getInputStream();
        if (compression) {
            return new GZIPInputStream(inputStream);
        }
        return inputStream;
    }

    @Override
    public void delete(String filePath, boolean compression) throws IOException {
        if (compression) {
            filePath += ".gz";
        }
        URI uri = buildUrl(filePath);

        HttpURLConnection connection = (HttpURLConnection) uri.toURL().openConnection();
        connection.setRequestMethod("DELETE");
        setupTimeout(connection);
        setupAuth(connection);
        setupConnection(connection);

        int responseCode = connection.getResponseCode();
        if (responseCode != 200) {
            throw new IOException("DELETE request failed with response code: " + responseCode);
        }
    }

    @Override
    public long length(String filePath, boolean compression) throws IOException {
        if (compression) {
            filePath += ".gz";
        }
        URI uri = buildUrl(filePath);

        HttpURLConnection connection = (HttpURLConnection) uri.toURL().openConnection();
        connection.setRequestMethod("HEAD");
        setupTimeout(connection);
        setupAuth(connection);
        setupConnection(connection);

        int responseCode = connection.getResponseCode();
        if (responseCode == 404) {
            return -1L;
        }
        if (responseCode != 200) {
            throw new IOException("LENGTH request failed with response code: " + responseCode);
        }
        return connection.getContentLengthLong();
    }
}
